home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC Graphics Unleashed
/
PC Graphics Unleashed.iso
/
ch03
/
io.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-05-21
|
26KB
|
991 lines
/****************************************************************
* FILE: io.c
* DESC: These are the basic input/output routines. They include
* disk-handling and keyboard I/O.
*
* HISTORY: Created 3/11/1993
* LAST CHANGED: 5/ 6/1993
*
* Copyright (c) 1993 by Scott Anderson
*
****************************************************************/
/* ----------------------INCLUDES----------------------------- */
#include <conio.h>
#include <stdio.h>
#include <io.h>
#include <dos.h> /* for the mouse */
#include <math.h>
#include <graph.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
#include "define.h"
/* ----------------------DEFINES------------------------------ */
/* 0-99 (for tweens) appended to name */
#define MAX_NAME_SIZE (8-2)
/**** PCX constants ****/
/* Set top 2 bits for the count bytes */
#define C0 (0xc0)
/* The inverse of C0: 0x3f = 63 */
#define MAX_RUN (0x3f)
/* Signals a 256 color palette */
#define PAL_CODE 12
/* -----------------------MACROS------------------------------ */
/* ----------------------TYPEDEFS----------------------------- */
typedef struct {
char manufacturer;
char version;
char encoding;
char bperpixelperplane;
unsigned int xmin, ymin, xmax, ymax;
unsigned int hres, vres;
char rgbmap[3][16];
char reserved;
char nplanes;
unsigned int bytesperline;
unsigned int paletteinfo;
}
CORE_PCX_HEADER;
typedef struct {
CORE_PCX_HEADER pcx;
unsigned char filler[128-sizeof (CORE_PCX_HEADER)];
}
HEADER_PCX;
/* ----------------------PROTOTYPES--------------------------- */
char lineAsk(char *name);
void setTextMode();
void setGraphicsMode();
int fixFilename(char *userName, char *fixedName,
char *extension, int number);
LINE_LIST *loadLines(char *filename, char *extension);
int saveLines(char *filename, LINE_LIST *lineList,
char *extension);
LINKED_LIST *appendName(LINKED_LIST *head, char *name);
LINKED_LIST *rootSequence(int argc, char *argv[]);
int waitForKey();
/* ----------------------GLOBAL DATA-------------------------- */
int CurrentPal = 0; /* ID of current palette */
int Wait = 0;
int Key;
int EndWait = OFF;
/* from the last read file, used to save pic */
static HEADER_PCX Header;
/* Global values set from last picture loaded */
int Xmin, Ymin, Xmax, Ymax;
/* no file output for NULL name */
char *OutFilename = NULL;
long TotalBytes;
int Button;
int Keystroke;
/*************** The file handling routines *****************/
/*****************************************************************
* FUNC: int saveScreen(PALETTE *pal)
*
* DESC: Save the current screen with the given palette
*****************************************************************/
int
saveScreen(PALETTE *pal)
{
static int fileCount = 0;
unsigned int byteCount = 0;
unsigned char lastColor;
unsigned char color;
/* The number of bytes in a run */
unsigned char num;
int index;
int x, y;
/* big enough for number & '.PCX' */
char pcxName[MAX_PATHLEN];
FILE *fp;
char *p;
if (!OutFilename) /* no file name on command line */
return 0;
/* Create a numbered file name for output */
fileCount++;
fixFilename(OutFilename, pcxName, EXT_PCX, fileCount);
/* Open the file and print the PCX file header */
if ((fp = fopen (pcxName, "wb")) == NULL)
quit (WRITE_OPEN_ERR, pcxName);
byteCount = fwrite (&Header, 1, sizeof (Header), fp);
if (byteCount != sizeof (Header))
quit (WRITE_ERR, pcxName);
/* pack the the screen lines before writing them */
for (y = Ymin; y <= Ymax; y++) {
num = 1; /* prime algorithm with the 1st pixel */
lastColor = _getpixel (Xmin, y);
for (x = Xmin + 1; x <= Xmax; x++) {
color = _getpixel (x, y);
if (color == lastColor) {
num++; /* Accumulate same-colored pixels */
if (num == MAX_RUN) {
byteCount += putBlock (num, lastColor, fp);
num = 0;
}
}
else { /* This is a pixel of a different color */
if (num)
byteCount += putBlock (num, lastColor, fp);
lastColor = color;
num = 1;
}
}
/* flush the last byte or batch of bytes on a line */
if (num)
byteCount += putBlock (num, lastColor, fp);
}
/* Finally, the color palette */
num = PAL_CODE; /* Write out the palette code byte */
byteCount += writeByte (&num, fp);
/* Write the color palette */
for (index = 0; index < COLORS; index++) {
color = pal->c[index].r << 3;
byteCount += writeByte (&color, fp);
color = pal->c[index].g << 3;
byteCount += writeByte (&color, fp);
color = pal->c[index].b << 3;
byteCount += writeByte (&color, fp);
}
fclose (fp);
return byteCount;
}
/*****************************************************************
* FUNC: int putBlock(unsigned char num, unsigned char color,
* FILE *fp)
*
* DESC: Write out the 2 or 1 byte block for Run Length Encoding.
*****************************************************************/
int
putBlock(unsigned char num, unsigned char color, FILE *fp)
{
unsigned int byteCount = 0;
/* Singlet colors with the top 2 bits set could be
confused with a count, so first write the one-count. */
if ((num > 1) || ((num == 1) && ((color & C0) == C0))) {
num |= C0;
byteCount += writeByte (&num, fp);
}
byteCount += writeByte (&color, fp);
return byteCount;
}
/*****************************************************************
* FUNC: int writeByte(unsigned char *byte, FILE *fp)
*
* DESC: Write one byte to the file and quit if there is an error.
*****************************************************************/
int
writeByte(unsigned char *byte, FILE *fp)
{
int count;
count = fwrite (byte, sizeof (char), 1, fp);
if (count != 1)
quit (WRITE_ERR, "");
return count;
}
/*****************************************************************
* FUNC: loadPicture (char *filename)
*
* DESC: Read a PCX file from the disk into a buffer
*****************************************************************/
PICTURE *loadPicture (char *filename)
{
char pcxName[MAX_PATHLEN];
FILE *fp;
int num, count;
PICTURE *picture;
unsigned int bytes_read;
unsigned char byte;
unsigned char far *bufptr;
fixFilename(filename, pcxName, EXT_PCX, 0);
fp = fopen(pcxName, "rb");
if (fp == NULL)
quit (READ_OPEN_ERR, pcxName);
picture = malloc(sizeof (*picture));
mustRead(fp, (char *) &Header, sizeof Header);
Xmin = picture->xmin = Header.pcx.xmin;
Ymin = picture->ymin = Header.pcx.ymin;
Xmax = picture->xmax = Header.pcx.xmax;
Ymax = picture->ymax = Header.pcx.ymax;
picture->tall = Ymax - Ymin + 1;
picture->wide = Xmax - Xmin + 1;
picture->pal_id = 0;
if (picture->tall != MAX_TALL
|| picture->wide != MAX_WIDE
|| Header.pcx.bperpixelperplane != 8
|| Header.pcx.nplanes != 1)
quit (WRONG_PCX_FILE, pcxName);
TotalBytes = picture->tall * (long) picture->wide;
TotalBytes = MIN(TotalBytes, MAX_BYTES);
picture->pixmap = (unsigned char far *)
_fmalloc((size_t) TotalBytes);
if (picture->pixmap == NULL)
return(NULL);
bufptr = picture->pixmap;
bytes_read = 0;
while (getBlock(&byte, &count, fp) != EOF) {
if ((bytes_read += count) > TotalBytes)
break;
for (num = 0; num < count; num++)
*bufptr++ = byte;
}
if (Header.pcx.version == 5)
loadPalette(fp, &picture->pal);
else
defaultPalette(&picture->pal);
fclose(fp);
return picture;
}
/*****************************************************************
* FUNC: getBlock (unsigned char *byte, int *count, FILE *fp)
*
* DESC: get a Run Length Encoded block. It's either a byte or a
* string of bytes with a length given by count.
*****************************************************************/
int
getBlock (unsigned char *byte, int *count, FILE *fp)
{
unsigned char input_byte;
if (fread(&input_byte, sizeof(input_byte), 1, fp) != 1)
return (EOF);
*count = 1; /* so far */
if ((input_byte & C0) == C0) {
/* Top 2 bits on, data stream to follow */
/* mask out the byte count */
*count = input_byte & MAX_RUN;
mustRead(fp, &input_byte, 1);
}
*byte = input_byte; /* return the byte */
return (0); /* success */
}
/*****************************************************************
* FUNC: int mustRead(FILE *fp, char *buf, int n)
*
* DESC: Read bytes or quit.
*****************************************************************/
int
mustRead(FILE *fp, char *buf, int n)
{
int nread;
nread = fread(buf, sizeof(*buf), n, fp);
if (nread != n)
quit(READ_ERR, "");
}
/*****************************************************************
* FUNC: int loadPalette(FILE *fp, PALETTE *palette)
*
* DESC: Go to the end of the file and get the palette
*****************************************************************/
loadPalette(FILE *fp, PALETTE *palette)
{
unsigned char packed_pal[3*COLORS];
int color, component;
int red, green, blue;
unsigned char input_byte;
fseek(fp, -(PALETTE_SIZE+1L), SEEK_END); /* -1 on error */
mustRead(fp, &input_byte, 1);
if (input_byte != PAL_CODE) {
printf("The color palette is missing!\n");
return (ERROR);
}
mustRead(fp, packed_pal, sizeof packed_pal);
for (component = 0, color = 0;
component < COMPS*COLORS; component += COMPS) {
palette->c[color].r = packed_pal[component+0]>>3;
palette->c[color].g = packed_pal[component+1]>>3;
palette->c[color].b = packed_pal[component+2]>>3;
color++;
}
palette->c[255].r = 0x1f;
palette->c[255].g = 0x1f;
palette->c[255].b = 0x1f;
return (0);
}
/*****************************************************************
* FUNC: int freePicture(PICTURE *pic)
*
* DESC: Free the memory allocated for this image.
*****************************************************************/
int
freePicture(PICTURE *pic)
{
_ffree(pic->pixmap);
free(pic);
}
/*****************************************************************
* FUNC: LINE_LIST *loadLines(char *filename, char *extension);
*
* DESC: allocate and fill in LINE_LIST structure from file
*****************************************************************/
LINE_LIST
*loadLines(char *filename, char *extension)
{
char lineName[MAX_PATHLEN];
FILE *fp;
char *p;
LINE_LIST *lineList;
int number;
int i;
lineList = malloc(sizeof (*lineList));
lineList->number = 0;
/* condition the filename to be a line list file name */
fixFilename(filename, lineName, extension, 0);
lineList->filename = strdup(lineName);
fp = fopen(lineName, "r");
if (fp == NULL)
return lineList;
if (fscanf(fp, "%d\n", &number) != 1
|| number < 1 || number > MAX_LINES) {
fclose(fp);
return lineList;
}
lineList->number = number;
for(i=0;i<number;i++) {
if (fscanf(fp, "%d %d %d %d\n",
&lineList->line[i].p[0].x,
&lineList->line[i].p[0].y,
&lineList->line[i].p[1].x,
&lineList->line[i].p[1].y) != 4)
quit (READ_CONTENTS_ERR, lineName);
lineList->line[i].p[0].x = clip(lineList->line[i].p[0].x,
0, MAX_WIDE);
lineList->line[i].p[0].y = clip(lineList->line[i].p[0].y,
0, MAX_TALL);
lineList->line[i].p[1].x = clip(lineList->line[i].p[1].x,
0, MAX_WIDE);
lineList->line[i].p[1].y = clip(lineList->line[i].p[1].y,
0, MAX_TALL);
}
fclose(fp);
return lineList;
}
/*****************************************************************
* FUNC: saveLines(char *filename, LINE_LIST *lineList,
* char *extension);
*
* DESC: save lineList to a file
*****************************************************************/
saveLines(char *filename, LINE_LIST *lineList, char *extension)
{
/* big enough for number & '.PCX' */
char lineName[MAX_PATHLEN];
FILE *fp;
char *p;
int i;
/* condition the filename to be a line list file name */
strcpy (lineName, filename);
strupr (lineName);
p= strstr(lineName, ".");
if (p == NULL)
p = lineName + strlen(lineName);
strcat(p, extension); /* add extension, it's needed */
fp = fopen(lineName, "w");
if (fp == NULL)
quit (WRITE_ERR, lineName);
fprintf(fp, "%d\n", lineList->number);
for(i=0;i<lineList->number;i++) {
fprintf(fp, "%d %d %d %d\n",
lineList->line[i].p[0].x,
lineList->line[i].p[0].y,
lineList->line[i].p[1].x,
lineList->line[i].p[1].y);
}
fclose(fp);
}
/*****************************************************************
* FUNC: LINKED_LIST *rootSequence(int argc, char *argv[])
*
* DESC: Process argument list and return a sequence of pcx files
*****************************************************************/
LINKED_LIST
*rootSequence(int argc, char *argv[])
{
LINKED_LIST *head = NULL;
int arg, loaded, i;
char filename[100];
for (arg = 1; arg < argc; arg++) {
if (fixFilename(argv[arg], filename, EXT_PCX, 0))
head = appendName(head, filename);
else {
loaded = 0;
for(i=1;i<MAX_TWEENS;i++) {
fixFilename(argv[arg], filename, EXT_PCX, i);
if(_access(filename, 4) == 0) {
head = appendName(head, filename);
loaded = 1;
}
else if (loaded == 1)
return head; /* a break in the sequence */
}
/* didn't find a sequence, try using EXT_PCX */
fixFilename(argv[arg], filename, EXT_PCX, 0);
head = appendName(head, filename);
}
}
return head;
}
/*****************************************************************
* FUNC: LINKED_LIST *appendName(LINKED_LIST *head, char *name)
*
* DESC: add a data item to the end of the linked list
*****************************************************************/
LINKED_LIST
*appendName(LINKED_LIST *head, char *name)
{
LINKED_LIST *l;
LINKED_LIST *end;
l = (LINKED_LIST *) malloc(sizeof (LINKED_LIST));
l->str = strdup(name);
l->next = NULL;
if (head == NULL)
return l;
/* find guy that points to end */
for(end=head; end->next; end=end->next);
end->next = l;
return head;
}
/*****************************************************************
* FUNC: int fixFilename(char *userName, char *fixedName,
* char *extension, int number)
*
* DESC: Create a valid filename with correct extension & number.
* Returns true if the userName had asn extension at end
*****************************************************************/
int
fixFilename(char *userName, char *fixedName, char *extension,
int number)
{
char *p;
char localbuf[100];
int extensionFound;
strcpy (localbuf, userName);
p = localbuf + strlen(localbuf) - 4;
/* extension is there */
extensionFound = strcmpi(p, extension) == 0;
if (extensionFound)
*p = '\0';
if (number)
sprintf(fixedName, "%s%d%s", localbuf, number, extension);
else
sprintf(fixedName, "%s%s", localbuf, extension);
return extensionFound;
}
/*** These are the color and the screen handling routines ***/
/*****************************************************************
* FUNC: int setPalette(PALETTE *palette)
*
* DESC: Set the 256 colors in the palette, using the output ports
*****************************************************************/
setPalette(PALETTE *palette)
{
int color;
for (color = 0; color < COLORS; color++) {
_outp (0x3c7, color-1);
_outp (0x3c9, palette->c[color].r << 1);
_outp (0x3c9, palette->c[color].g << 1);
_outp (0x3c9, palette->c[color].b << 1);
}
}
/*****************************************************************
* FUNC: int defaultPalette(PALETTE *palette)
*
* DESC: Create a gray-scale palette as a default
*****************************************************************/
defaultPalette(PALETTE *palette)
{
int i;
long j;
long k;
for (i=0;i<COLORS;i++) {
j = i & 0x1f;
palette->c[i].r = palette->c[i].g = palette->c[i].b = j;
}
}
/*****************************************************************
* FUNC: int displayPicture(PICTURE *picture)
*
* DESC: set the video mode & palette, then draw from the buffer
* to the screen.
*****************************************************************/
displayPicture(PICTURE *picture)
{
int x, y, xb, yb;
if (picture->pal_id == 0) { /* need to define palette */
picture->pal_id = paletteID(&picture->pal);
}
if (CurrentPal != picture->pal_id) {
setPalette(&picture->pal);
CurrentPal = picture->pal_id;
}
displayNoPal(picture);
}
/*****************************************************************
* FUNC: int displayNoPal(PICTURE *picture)
*
* DESC: display a picture without messing with the palette
*****************************************************************/
int
displayNoPal(PICTURE *picture)
{
/* This is the memory address of the VGA screen */
unsigned char far *screen = (unsigned char far *) 0xa0000000;
unsigned char far *bufptr = picture->pixmap;
_fmemcpy (screen, bufptr, (unsigned int) MAX_BYTES);
wait(Wait);
}
/*****************************************************************
* FUNC: int paletteID(PALETTE *pal)
*
* DESC: return a unique id for each unique palette
*****************************************************************/
int
paletteID(PALETTE *pal)
{
static PALETTE *definedPals[MAX_FILES];
static int npals = 0;
int i;
for (i = 0; i < npals; i++) {
if (samePal(definedPals[i], pal))
return i+1;
}
definedPals[npals] = pal;
npals++;
return npals;
}
/*****************************************************************
* FUNC: int samePal(PALETTE *p1, PALETTE *p2)
*
* DESC: return 1 if same exact palettes, 0 otherwise
*****************************************************************/
int
samePal(PALETTE *p1, PALETTE *p2)
{
int index;
for (index = 0; index < COLORS; index++) {
if (p1->c[index].r != p2->c[index].r) return 0;
if (p1->c[index].g != p2->c[index].g) return 0;
if (p1->c[index].b != p2->c[index].b) return 0;
}
return 1;
}
/*****************************************************************
* FUNC: int drawPalette()
*
* DESC: Draw the 256 colors of the current palette in the top
* left of the screen.
*****************************************************************/
int
drawPalette()
{
#define BT 4 /* block tall */
#define BW 6 /* block wide */
int x, y, xb, yb;
for (xb = 0; xb < 16; xb++) {
for (yb = 0; yb < 16; yb++) {
for (x = xb*BW; x < (xb*BW + BW); x++) {
for (y = yb*BT; y < (yb*BT + BT); y++) {
_setcolor(yb * 16 + xb);
_setpixel (x, y);
}
}
}
}
}
/******************** The mouse routines ********************/
/*****************************************************************
* FUNC: int initMouse()
*
* DESC: Initialize the mouse and quit if a driver isn't present.
*****************************************************************/
#define MOUSE 0x33
int
initMouse()
{
union _REGS regs;
struct _SREGS sregs;
regs.x.ax = 0x3533;
_intdosx(®s, ®s, &sregs);
if((regs.x.bx | sregs.es ) == 0)
quit(MOUSE_ERR, "");
regs.x.ax = 0;
_int86(MOUSE, ®s, ®s);
}
/*****************************************************************
* FUNC: int hideMouse()
*
* DESC: Hide the mouse cursor before we draw anything.
* This use the chunk of screen saved by showMouse.
*****************************************************************/
int
hideMouse()
{
union _REGS regs;
regs.x.ax = 0x02;
_int86(MOUSE, ®s, ®s);
}
/*****************************************************************
* FUNC: int showMouse()
*
* DESC: Redisplay the mouse cursor after drawing something on the
* screen. Save the chunk of screen under the cursor for
* hideMouse.
*****************************************************************/
int
showMouse()
{
union _REGS regs;
regs.x.ax = 0x01;
_int86(MOUSE, ®s, ®s);
}
/*****************************************************************
* FUNC: int mousePos(int *x, int *y)
*
* DESC: Return the x,y coordinates of the mouse and any
* button or keystroke.
*****************************************************************/
#define KEYHIT 0x0b /* Check the keyboard */
#define STDIN 0x07 /* Read a char from standard input */
int
mousePos(int *x, int *y)
{
union _REGS regs;
char *buttons;
int keystroke;
regs.h.ah = KEYHIT;
_intdos(®s, ®s);
keystroke = (regs.h.al == 0xff) ? KEYPRESS : 0;
if (keystroke) {
regs.h.ah = STDIN;
_intdos(®s, ®s); /* STDIN */
if (Key = regs.h.al == ESC)
quit(0,""); /* quit on Esc key */
}
regs.x.ax = 0x03;
_int86(MOUSE, ®s, ®s);
*x = regs.x.cx/2; /* store the x & y coordinates */
*y = regs.x.dx;
/* return: 0 = none
1 = left
2 = right
4 = keypress */
return (Button = (regs.x.bx & 0x03) | keystroke);
}
/****************** A few utility routines ******************/
/*****************************************************************
* FUNC: int waitForKey()
*
* DESC: Wait for a key to be pressed, then return the key.
*****************************************************************/
int
waitForKey()
{
int key;
while (!_kbhit());
key = _getch();
if (key == ESC)
quit(0,"");
return (key); /* clear buffer and return keystroke */
}
/*****************************************************************
* FUNC: char lineAsk(char *name)
*
* DESC: Ask the users if they want to use the pre-defined lines.
*****************************************************************/
char
lineAsk(char *name)
{
char c;
setTextMode();
_settextposition(VTAB, HTAB);
printf ("The picture '%s' has some pre-defined", name);
_settextposition(VTAB+2, HTAB);
printf ("control lines. Would you like to use them?");
_settextposition(VTAB+4, HTAB+8);
printf ("(Y/N):");
c = getche();
if (c == ESC)
quit(NO_ERROR, "");
c = toupper(c);
return c;
}
/*****************************************************************
* FUNC: int quitCheck()
*
* DESC: Check keyboard. If there is no key waiting, return 0,
* for OK. If a number from 1-9 is typed, change the wait
* between frames. Otherwise, the user wants to quit,
* so return 1.
*****************************************************************/
int
quitCheck()
{
static int spaceWait = OFF;
if (spaceWait) {
Key = getch();
if (Key != ' ')
spaceWait = OFF;
}
else if (_kbhit()) {
Key = _getch();
if (Key == ' ') {
/* turn on space bar stepping */
spaceWait = ON;
/* pause for space key */
Key = _getch();
}
if (Key == ESC || Key == 'q' || Key == 'Q')
return 1; /* a quit key */
if (Key == 'E' || Key == 'e')
EndWait ^= ON; /* toggles on and off */
else if (Key >= '1' && Key <= '9')
Wait = '9' - Key;
}
return 0;
}
/****************************************************************/
beep()
{
printf("\a");
}
/*****************************************************************
* FUNC: int clip(int num, int min, int max)
*
* DESC: Clip the number to a value between min and max, inclusive.
*****************************************************************/
int
clip(int num, int min, int max)
{
if (num < min)
num = min;
else if (num > max)
num = max;
return num;
}
/*****************************************************************
* FUNC: int waitForTop() and waitForBottom()
*
* DESC: These routines wait for the TV scan line to get to the
* top and the bottom of the screen.
*****************************************************************/
#define VID_PORT 0x3da
#define VID_RETRACE 0x8
waitForTop()
{
/* wait till retrace ends */
while(_inp(VID_PORT) & VID_RETRACE);
}
waitForBottom()
{
/* wait till next retrace starts*/
while(!(_inp(VID_PORT) & VID_RETRACE));
}
/*****************************************************************
* FUNC: int wait(int count)
*
* DESC: Wait for a number of screen refreshes, based on count.
*****************************************************************/
int
wait (int count)
{
int i;
if (!count)
return 0;
for (i = 1; i < 2 * count; i++) {
waitForBottom();
waitForTop();
}
}
/*****************************************************************
* FUNC: void setGraphicsMode()
*
* DESC: Set up the graphics screen
*****************************************************************/
void
setGraphicsMode()
{
_setvideomode(_MRES256COLOR);
CurrentPal = 0; /* Force a new palette */
}
/*****************************************************************
* FUNC: void setTextMode()
*
* DESC: Set the screen to the startup text mode
*****************************************************************/
void
setTextMode()
{
CurrentPal = 0; /* Force a new palette */
_setvideomode(_DEFAULTMODE);
}
/*****************************************************************
* FUNC: void quit(int err, char *name)
*
* DESC: Turn text back on, print the error message and quit.
*****************************************************************/
void
quit(ERR err, char *name)
{
static char *ErrMess[] = {
" ",
"I can't get enough memory. Try turning off some TSR's.",
"I can't find the file",
"I can't read the file",
"I can't open the file",
"I can't write the file",
"You must install a mouse driver to run this program.",
"This is the wrong PCX type - I can't read the file",
};
setTextMode();
if (err != NO_ERROR) {
printf ("Error #%d:\n", err);
printf ("%s %s\n", ErrMess[err], name);
exit (err);
}
exit (0);
}